Ruby on Rails で動画をアップロード – CarrierWaveとFFmpeg
はじめに
ブラウザよりスマホのカメラを起動して動画を撮影し、その動画をアップロードするアプリを
Ruby on Railsで構築する方法について調べて、サンプルを作成してみました。
以下、今回参考にさせて頂いた記事です。
Railsで動画ファイルを管理する~CarrierWave Flowplayer
Railsで動画を管理する2 動画のサムネイルを作成する~CarrierWave + Flowplayer
上記の記事を参考にし、サンプルでは以下の点について主に変更しています。
・twitter bootstrap の適用。
・アップロードした動画のスクリーンショットを撮るときにサイズを指定。
・ブラウザよりiPhoneのビデオが起動するように、HTMLタグを変更。
・FlowPlayerの使用を止め、リンクをクリックした場合に、動画の再生はデバイスに任せることにした。
尚、今回は動画をアップロードしていますが、カメラ等で撮影した静的な画像も
同じ方法でアップロードできます。(後述します)
動作環境は、以下の通りです。
・iPhone5、ブラウザはSafari
・Ruby 2.1
・Rails 4.1
実装について
サンプルの主な作成手順について書いて行きたいと思います。
$ rails new アプリ名、等でアプリの雛形はできていることが前提です。
1.twitter bootstrapの適用
twitter bootstrapのインストール
Gemfileに以下を記述し、bundle installします。
gem "twitter-bootstrap-rails" gem "less-rails" gem 'therubyracer'
twitter bootstrapの適用
以下のコマンドを実行します。
$ rails g bootstrap:install $ rails g bootstrap:layout application fixed
application.html.erbを上書きしますか(超訳)、的なことを聞かれるので、「Y」を押下します。
ヘッダー部のバーとコンテンツが重なるのを防ぐ設定をする
app\assets\stylesheets\bootstrap_and_overrides.css.less に以下を追加します。
body { padding-top: 60px; }
(気になる人は・・・)sidebarを消す
app/views/layouts/application.html.erb のsidebarを消去するため
<div class="span3">・・・</div>
を消去します。
2.CarrierWaveによる動画のアップロード
CarrierWaveというgemを使用すると、ファイルのアップロードが簡単にできます。
以下に手順を記載しますが、「ブラウザよりビデオを起動するようにする」以外は、上記の参考サイトと手順はほぼ同じです。
CarrierWaveのインストール
Gemfileに以下を記述し、bundle installします。
gem 'carrierwave'
アップローダーの作成
generatorを使い、アップローダーとなるクラスを作成します。
$ rails g uploader video
app/uploaders/video_uploader.rb
が作成されます。
スキャフォールド
今回は「article」というModelを作成し、「video」カラムに
動画の情報を登録することにします。
$ rails g scaffold article title video $ rake db:migrate
モデルとアップローダーの紐付け
モデルクラスのapp/models/article.rb に以下を追加します。
mount_uploader :video, VideoUploader
アップロードを許可する拡張子を設定
上記で作成したアップローダーである、app/uploaders/video_uploader.rbを編集し
def extension_white_list~end のコメントアウトは外し、MOV wmv を追加しました。
(iPhoneで撮影した動画、Windows Media Playerの動画をアップロードするため)
# Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list %w(jpg jpeg gif png MOV wmv) end
Modelに入力必須属性を追加
モデルクラスのapp/models/article.rb に以下を追加します。
validates :title, :video, :presence => true
root画面を定義
config/routes.rb に以下を追加します。
root 'articles#index'
ブラウザよりビデオを起動するようにする
iPhoneのブラウザ(Safariで動作を確認)にてボタン押下時にビデオが起動するよう
入力フィールドを編集します。
(PCのブラウザでは、アップロードするファイルの種類が動画になるようです。)
具体的には、/app/views/articles/_form.html.erbの
<%= f.text_field :video %> を
<%= f.file_field :video, :accept => 'video/*' %>
に変更し、以下のようにしました。
<div class="field"> <%= f.label :video %><br> <%= f.file_field :video, :accept => 'video/*' %> </div>
ここで「:accept => 'video/*」を「:accept => ‘image/*」にすると
カメラが起動するようになります。
3.FFmpeg
FFmpegを使用し、アップロードする動画のスクリーンショットを作成します。
スクリーンショットは、下記「4.スクリーンショットの表示」にて詳細画面にリンクとして表示し
クリック時に動画が再生できるようにします。
インストール
FFmpegがマシンにインストールされていない場合、gemでインストールする前に
マシンにインストールする必要があります。
Ubuntu $ sudo apt-get install ffmpeg Mac $ brew install ffmpeg
Gemfileに以下を記述し、bundle installします。
gem 'streamio-ffmpeg'
スクリーンショットの作成
アップローダーの app/uploaders/video_uploader.rbを編集し、FFmpegを使ってスクリーンショットを作成するようにします。
尚、この処理も、スクリーンショットを作成するscreenshot()メソッドがアップローダーの中に書かれている以外は
上記の参考サイトと処理の流れが大体同じです。
require 'streamio-ffmpeg'
を追加し
# Create different versions of your uploaded files:
の下に、スクリーンショットを作成する処理を実装します。
# Create different versions of your uploaded files: (中略) version :screenshot do process :screenshot def full_filename (for_file = model.logo.file) "screenshot.jpg" end end def screenshot tmpfile = File.join(File.dirname(current_path), "tmpfile") File.rename(current_path, tmpfile) movie = FFMPEG::Movie.new(tmpfile) movie.screenshot(current_path + ".jpg", {resolution: '512x312' }, preserve_aspect_ratio: :width) preserve_aspect_ratio: :width) File.rename(current_path + ".jpg", current_path) File.delete(tmpfile) end
「# Create different versions of your uploaded files: 」とあるように
アップロードするファイルの別バージョンを作成する処理を、「version・・・」で
記述することができます。
4行目で、スクリーンショットを作成するプロセスとして「screenshot」を指定しています。
この「screenshot」の処理の実態は、10行目以降のscreenshot()メソッドです。
5行目のfull_filename()メソッド内で、実際に作成するスクリーンショットのファイル名を指定しています。
10行目以降のscreenshot()について説明する前に、CarrierWaveがファイルをアップロードする際の
ファイル操作について説明しておきます。
CarrierWaveはファイルをアップロードする前に、tmpフォルダにファイルを一旦格納します。
その後、最終的な保存先(デフォルトではpublic/uploads/モデル名/videoフォルダ)にファイルをアップロードします。
screenshot()では、tmpフォルダ内で以下の手順でスクリーンショットを作成しています。
・CarrierWaveによってtmpフォルダ内に作られる「screenshot・・・」という動画ファイルのファイル名を
「tmpfile」に変える。
・「tmpfile」よりスクリーンショットを作成する。この時にサイズを指定する。
・スクリーンショットのファイル名を、アップロードするファイル名 + ".jpg"にする。
・「tmpfile」を削除する。
これらの処理をtmpフォルダ内で行うと、CarrierWaveが最終的な保存先に動画ファイル、スクリーンショットとも
移動してくれます。
更新時にスクリーンショットを再作成するよう、モデルを変更
Controllerの app/controllers/articles_controller.rb 内に
「@article.video.recreate_versions!」を追加し、update時にrecreate_versions!を呼び出します。(3行目)
def update respond_to do |format| if @article.update(article_params) && @article.video.recreate_versions! format.html { redirect_to @article, notice: 'Article was successfully updated.' } format.json { head :no_content } else format.html { render action: 'edit' } format.json { render json: @article.errors, status: :unprocessable_entity } end end end
上記の処理を行わないと、上記で作成したスクリーンショットが、最終的な保存先にコピーされない
事象が発生しました。
参考記事
CarrierWave with custom processor not registering
4.スクリーンショットの表示
アップロードした動画のスクリーンショットを画面に表示し、クリック時に再生するようにします。
先に書いた通り、再生はデバイス(PCやiPhoneなど)に任せることしました。
app/views/articles/show.html.erb
<p> <strong>Video:</strong> </p> <p> <%= link_to @article.video_url.to_s do %> <%= image_tag(@article.video_url(:screenshot).to_s, id: "video", :alt => "screenshot") %> <% end %> </p>
スキャフォールドによって作成されたViewを編集しています。
「<%= link_to @article.video_url.to_s do %>」で動画のURLのリンクを作成し
「image_tag・・・」でスクリーンショットを表示するように指定しています。
5.完成した画面
完成したアプリは、以下のような画面となります。
登録画面(iPhoneのSafariで見た場合)
表示画面
※横向きになっていますが、そういう風に撮影した動画です。(こんなのしか手元になかった)
まとめ
Railsで動画や画像をアップロードする機能を作成する際の
参考になれば幸いです。
今回作成したアプリは、以下のGitHubに上げてあります。
carrierwave_ffmpeg_sample